﻿using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using System.Linq.Expressions;
using System.Reflection;


namespace App.Core.Extensions
{
    public static class SoftDeleteQueryExtension
    {
        public static void AddSoftDeleteQueryFilter(
            this IMutableEntityType entityData)
        {
            var methodToCall = typeof(SoftDeleteQueryExtension)
                .GetMethod(nameof(GetSoftDeleteFilter),
                    BindingFlags.NonPublic | BindingFlags.Static)
                .MakeGenericMethod(entityData.ClrType);
            var filter = methodToCall.Invoke(null, new object[] { });
            entityData.SetQueryFilter((LambdaExpression)filter);
        }

        private static LambdaExpression GetSoftDeleteFilter<TEntity>()
            where TEntity : class, ISoftDelete
        {
            Expression<Func<TEntity, bool>> filter = x => !x.IsDeleted;
            return filter;
        }
    }

    public class SaveChangesInterceptor : ISaveChangesInterceptor
    {
        public SaveChangesInterceptor(IHttpContextAccessor httpContextAccessor)
        {
        }

        public ValueTask<int> SavedChangesAsync(SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken = default)
        {
            var softDeletedEntries = eventData.Context!.ChangeTracker.Entries().Where(entry => entry.Metadata.FindRuntimeAnnotation(nameof(ISoftDelete.IsDeleted)) != null);
            foreach (var entries in softDeletedEntries)
            {
                entries.State = EntityState.Detached;
            }
            return ValueTask.FromResult(result);
        }

        public ValueTask<InterceptionResult<int>> SavingChangesAsync(
      DbContextEventData eventData,
      InterceptionResult<int> result,
      CancellationToken cancellationToken = default)
        {
            var context = eventData.Context!;
              
            ContextEntriesDefined(context);

            return ValueTask.FromResult(result);
        }

        private void ContextEntriesDefined(DbContext context)
        {
            // 获取所有状态为`Deleted`并被标记为软删除的实体
            var deletedEntities = context.ChangeTracker.Entries()
                .Where(entry => entry.State == EntityState.Deleted
                && entry.CurrentValues.Properties.Any(x => x.Name == nameof(ISoftDelete.IsDeleted)));

            foreach (var entry in deletedEntities)
            {
                entry.State = EntityState.Modified;
                entry.CurrentValues[nameof(ISoftDelete.IsDeleted)] = true;
            }

            var addEntities = context.ChangeTracker.Entries()
               .Where(entry => entry.State == EntityState.Added &&
                 entry.CurrentValues.Properties.Any(x => x.Name == nameof(AuditEntity<long>.CreationTime)));
            foreach (var entry in addEntities)
            {
                entry.CurrentValues[nameof(AuditEntity<long>.CreationTime)] = DateTime.Now;
            }

            var modifyEntities = context.ChangeTracker.Entries()
              .Where(entry => entry.State == EntityState.Modified &&
                 entry.CurrentValues.Properties.Any(x => x.Name == nameof(AuditEntity<long>.LastModifyTime)));
            foreach (var entry in modifyEntities)
            {
                entry.CurrentValues[nameof(AuditEntity<long>.LastModifyTime)] = DateTime.Now;
            }
        }

#if NET6_0
        public InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result) {

            var context = eventData.Context!;

            ContextEntriesDefined(context);

            return result;
        }
        public int SavedChanges(SaveChangesCompletedEventData eventData, int result)
        {
            var softDeletedEntries = eventData.Context!.ChangeTracker.Entries().Where(entry => entry.Metadata.FindRuntimeAnnotation(nameof(ISoftDelete.IsDeleted)) != null);
            foreach (var entries in softDeletedEntries)
            {
                entries.State = EntityState.Detached;
            }
            return result;
        }
        public void SaveChangesFailed(DbContextErrorEventData eventData) { }
        public Task SaveChangesFailedAsync(DbContextErrorEventData eventData, CancellationToken cancellationToken = default) => Task.CompletedTask;
#endif
    }

}
